package com.luych.toolbox.docker.service.service;

import com.luych.toolbox.common.feign.param.docker.service.DockerNetworkParam;
import com.luych.toolbox.common.feign.view.docker.service.DockerNetworkView;
import com.luych.toolbox.common.page.CommonPageResult;
import com.luych.toolbox.common.service.exception.HttpException;
import com.luych.toolbox.docker.service.constant.Constant;
import com.luych.toolbox.docker.service.dao.DockerServerDao;
import com.luych.toolbox.docker.service.entity.DockerServer;
import com.luych.toolbox.docker.service.sdk.DockerClient;
import com.luych.toolbox.docker.service.sdk.network.model.DockerNetworkInspectResult;
import com.luych.toolbox.docker.service.sdk.network.model.DockerNetworksCreateParam;
import com.luych.toolbox.docker.service.sdk.network.model.DockerNetworksCreateResult;
import com.luych.toolbox.docker.service.sdk.network.model.DockerNetworksResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

@Service
public class DockerNetworkService {

    private static final Logger logger = LoggerFactory.getLogger(DockerNetworkService.class);

    private final DockerServerDao dockerServerDao;

    @Autowired
    public DockerNetworkService(DockerServerDao dockerServerDao) {
        this.dockerServerDao = dockerServerDao;
    }

    private List<DockerNetworkView> convert(DockerNetworksResult dockerNetworksResult) {
        List<DockerNetworkView> dockerNetworkViews = new ArrayList<>();
        for (DockerNetworksResult.Network dockerNetworkResult : dockerNetworksResult) {
            DockerNetworkView dockerNetworkView = new DockerNetworkView();
            dockerNetworkView.setId(dockerNetworkResult.getId());
            dockerNetworkView.setName(dockerNetworkResult.getName());
            dockerNetworkView.setDriver(dockerNetworkResult.getDriver());
            dockerNetworkView.setCreated(dockerNetworkResult.getCreatedDate());
            Optional<DockerNetworksResult.Network.IPAM.Config> configOpt = Optional.of(dockerNetworkResult)
                    .map(DockerNetworksResult.Network::getIpam)
                    .map(DockerNetworksResult.Network.IPAM::getConfig)
                    .orElse(new ArrayList<>()).stream().findFirst();
            configOpt.map(DockerNetworksResult.Network.IPAM.Config::getSubnet).ifPresent(dockerNetworkView::setSubnet);
            configOpt.map(DockerNetworksResult.Network.IPAM.Config::getGateway).ifPresent(dockerNetworkView::setGateway);

            dockerNetworkViews.add(dockerNetworkView);
        }
        return dockerNetworkViews;
    }

    public CommonPageResult<DockerNetworkView> page(long pageSize, long pageNumber, String sort, String order, String serverId, String keyword) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());
        DockerNetworksResult dockerNetworksResult = dockerClient.networks().all();
        List<DockerNetworkView> dockerNetworkViews = this.convert(dockerNetworksResult);
        if (StringUtils.hasText(keyword)) {
            dockerNetworkViews.removeIf(v -> !v.getName().contains(keyword));
        }
        dockerNetworkViews.sort(Comparator.comparing(DockerNetworkView::getCreated));
        int from = (int) (pageSize * (pageNumber - 1));
        int to = (int) (pageSize * pageNumber);
        to = Math.min(to, dockerNetworkViews.size());
        return new CommonPageResult<>(dockerNetworkViews.subList(from, to), dockerNetworkViews.size());
    }

    public DockerNetworkView create(String serverId, DockerNetworkParam dockerNetworkParam) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        DockerNetworksCreateParam dockerNetworksCreateParam = new DockerNetworksCreateParam();
        dockerNetworksCreateParam.setName(dockerNetworkParam.getName());
        dockerNetworksCreateParam.setDriver(dockerNetworkParam.getDriver());
        if (StringUtils.hasText(dockerNetworkParam.getSubnet()) || StringUtils.hasText(dockerNetworkParam.getGateway())) {
            dockerNetworksCreateParam.setIpam(new DockerNetworksCreateParam.IPAM());
            dockerNetworksCreateParam.getIpam().setConfig(new ArrayList<>());
            dockerNetworksCreateParam.getIpam().getConfig().add(new DockerNetworksCreateParam.IPAM.Config());
            dockerNetworksCreateParam.getIpam().getConfig().get(0).setSubnet(dockerNetworkParam.getSubnet());
            dockerNetworksCreateParam.getIpam().getConfig().get(0).setGateway(dockerNetworkParam.getGateway());
        }

        DockerNetworksCreateResult dockerNetworksCreateResult = dockerClient.networks().create(dockerNetworksCreateParam);

        return this.get(serverId, dockerNetworksCreateResult.getId());
    }

    public void remove(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.networks().network(id).remove();
    }

    public DockerNetworkView get(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        DockerNetworkInspectResult dockerNetworkInspectResult = dockerClient.networks().network(id).inspect();
        DockerNetworkView dockerNetworkView = new DockerNetworkView();
        dockerNetworkView.setId(dockerNetworkInspectResult.getId());
        dockerNetworkView.setName(dockerNetworkInspectResult.getName());
        dockerNetworkView.setDriver(dockerNetworkInspectResult.getDriver());
        dockerNetworkView.setCreated(dockerNetworkInspectResult.getCreatedDate());
        Optional<DockerNetworkInspectResult.IPAM.Config> configOpt = Optional.of(dockerNetworkInspectResult)
                .map(DockerNetworkInspectResult::getIpam)
                .map(DockerNetworkInspectResult.IPAM::getConfig)
                .orElse(new ArrayList<>()).stream().findFirst();
        configOpt.map(DockerNetworkInspectResult.IPAM.Config::getSubnet).ifPresent(dockerNetworkView::setSubnet);
        configOpt.map(DockerNetworkInspectResult.IPAM.Config::getGateway).ifPresent(dockerNetworkView::setGateway);
        return dockerNetworkView;
    }
}
